//
// Created by Administrator on 2021/8/30.
//

#include "Stage.h"
#include "GameLib/Framework.h"

using namespace GameLib;

enum ObjEnum
{
    Empty = 0,
    Player = 1,
    Wall = 4,
    Box = 8,
    Target = 16
};

Stage::Stage(const char* map, size_t size)
    : win_{false}
{
    // calc width and height and fill data
    unsigned int w = 0, h = 0;
    const char* p = map;
    size_t count = 0;
    while(count <= size)
    {
        auto b = *p;
        if(*p == '\r' && *(p + 1) == '\n')
        {
            ++h;
            p += 2;
            continue;
        }
        else if(*p == '\r' || *p == '\n')
        {
            ++h;
            p += 1;
            continue;
        }
        if(h == 0)
        {
            ++w;
        }
        ++p;
        ++count;
    }
    ++h;
    auto* d = new int[w * h];
    count = 0;
    p = map;
    auto* td = d;
    while(count < w * h + 2 * h)
    {
        if(*p == '\r' && *(p + 1) == '\n')
        {
            p += 2;
            count += 2;
            continue;
        }
        switch (*p)
        {
            case ' ': *td = Empty; break;
            case '#': *td = Wall; break;
            case '.': *td = Target; break;
            case 'o': *td = Box; break;
            case 'p': *td = Player; break;
        }
        ++td;
        ++p;
        ++count;
    }
    // build data
    data_.setSize(w, h);
    for(auto j = 0; j < h; ++j)
    {
        for(auto i = 0; i < w; ++i)
        {
            data_(i, j) = d[w * j + i];
        }
    }
}

void Stage::update(char input)
{
    int dx = 0;
    int dy = 0;
    switch (input)
    {
        case 'w': --dy; break;
        case 's': ++dy; break;
        case 'a': --dx; break;
        case 'd': ++dx; break;
    }

    int px = 0;
    int py = 0;
    // find player
    for(auto j = 0; j < data_.h(); ++j)
    {
        for(auto i = 0; i < data_.w(); ++i)
        {
            if((data_(i, j) & Player) > 0)
            {
                px = i;
                py = j;
            }
        }
    }

    int tx = px + dx;
    int ty = py + dy;
    // cant move
    if(data_(tx, ty) == ObjEnum::Wall)
    {
        return;
    }
    if(data_(tx, ty) == ObjEnum::Box)
    {
        int bx = tx + dx;
        int by = ty + dy;
        if((data_(bx, by) & (ObjEnum::Wall | ObjEnum::Box)) > 0)
        {
            return;
        }
        data_(bx, by) |= ObjEnum::Box;
        data_(tx, ty) ^= ObjEnum::Box;
    }
    data_(tx, ty) |= ObjEnum::Player;
    data_(px, py) ^= ObjEnum::Player;

    for(auto j = 0; j < data_.h(); ++j)
    {
        for(auto i = 0; i < data_.w(); ++i)
        {
            if(data_(i, j) == Box)
            {
                return;
            }
        }
    }
    win_ = true;
}

void Stage::draw()
{
    auto vram = Framework::instance().videoMemory();
    auto windowWidth = Framework::instance().width();
    for(auto j = 0; j < data_.h(); ++j)
    {
        for(auto i = 0; i < data_.w(); ++i)
        {
            unsigned color = 0x0;
            auto b = data_(i, j);
            switch (data_(i, j))
            {
                case Empty: color = 0x0; break;
                case Player: color = 0x00ff00; break;
                case Wall: color = 0xffffff; break;
                case Box: color = 0xff0000; break;
                case Target: color = 0x0000ff; break;
                case Target | Player: color = 0x00ff11; break;
                case Target | Box: color = 0xff1100; break;
            }
            for(int ii = 0; ii < 16; ++ii)
            {
                for(int jj = 0; jj < 16; ++jj)
                {
                    vram[(j * 16 + ii) * windowWidth + (i * 16 + jj)] = color;
                }
            }
        }
    }
}